<!--
 * @Description: Backtop 回到顶部
 * @Author bianpengfei
 * @create 2021/5/30 16:31
 -->
<!--default-->
<script>
export default { name: 'backtop' }
</script>

<!--setup-->
<script setup>
import {nextTick, onActivated, onDeactivated, onMounted, onUnmounted, reactive} from 'vue'
import { Base } from '@bianpengfei/utils'
const { throttle } = Base

const cubic = value => Math.pow(value, 3)
const easeInOutCubic = value => (value < 0.5 ? cubic(value * 2) / 2 : 1 - cubic((1 - value) * 2) / 2)
let throttledScrollHandler = null

const props = defineProps({
  visibilityHeight: {
    type: Number,
    default: 200
  },
  target: [String]
})
const emit = defineEmits(['click'])
const state = reactive({
  el: null,
  container: null,
  visible: false
})

onMounted(() => {
  init()
  addEventListener()
  nextTick(() => {
    scrollToTop()
  })
})

onUnmounted(() => {
  removeEventListener()
})

onActivated(() => {
  addEventListener()
})

onDeactivated(() => {
  removeEventListener()
})

const addEventListener = () => {
  if (!throttledScrollHandler) throttledScrollHandler = throttle(onScroll, 10)
  state.container.addEventListener('scroll', throttledScrollHandler)
}

const removeEventListener = () => {
  state.container.removeEventListener('scroll', throttledScrollHandler)
}

/**
 * 点击
 * @param e
 */
const onHandleClick = e => {
  scrollToTop()
  emit('click', e)
}

/**
 * 监听滚动
 */
const onScroll = () => {
  const scrollTop = state.el.scrollTop
  state.visible = scrollTop >= props.visibilityHeight
}

/**
 * 初始化
 */
const init = () => {
  state.container = document
  state.el = document.documentElement
  if (props.target) {
    state.el = document.querySelector(props.target)
    if (!state.el) {
      throw new Error(`target is not existed: ${props.target}`)
    }
    state.container = state.el
  }
}

/**
 * 滚动到顶部
 */
const scrollToTop = () => {
  const el = state.el
  const beginTime = Date.now()
  const beginValue = el.scrollTop
  const rAF = window.requestAnimationFrame || (func => setTimeout(func, 16))
  const frameFunc = () => {
    const progress = (Date.now() - beginTime) / 500
    if (progress < 1) {
      el.scrollTop = beginValue * (1 - easeInOutCubic(progress))
      rAF(frameFunc)
    } else {
      el.scrollTop = 0
    }
  }
  rAF(frameFunc)
}
</script>

<!--render-->
<template>
  <transition name="fade-in">
    <div v-if="state.visible" @click.stop="onHandleClick" class="backtop">
      <i class="iconfont-bpf-m icon-m-dingbu1"></i>
      <div>顶部</div>
    </div>
  </transition>
</template>

<!--style-->
<style scoped lang="less">
.fade-in-enter-from,
.fade-in-leave-active {
  opacity: 0;
}
.fade-in-enter-active,
.fade-in-leave-active {
  -webkit-transition: all 0.3s cubic-bezier(0.55, 0, 0.1, 1);
  transition: all 0.3s cubic-bezier(0.55, 0, 0.1, 1);
}

.backtop {
  position: fixed;
  background-color: #fff;
  width: 0.84rem;
  height: 0.84rem;
  border-radius: 50%;
  color: @primary-color;
  display: flex;
  right: 0.3rem;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  cursor: pointer;
  z-index: 100;
  bottom: 1.2rem;
  padding: 0.04rem;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.2);
  i {
    font-size: 0.36rem;
  }
  div {
    font-size: 0.24rem;
  }
}
</style>
